/** 判断数字符号是否相同
 *
 * @param n1
 * @param n2
 * @returns boolean
 */
export function sameSymbol(n1: number, n2: number) {
  return (n1 ^ n2) >= 0;
}

/** 判断正整数是否是2的整数幂
 *
 * @param n 正整数
 * @returns boolean
 */
export function isPowerOfTwo(n: number) {
  return n > 0 && (n & (n - 1)) === 0;
}

/** 通过0~5数字获取星级评价
 *
 * @param n 0~5的整数
 * @returns string
 */
export function starRating(n: number) {
  const star = "★★★★★☆☆☆☆☆";
  const num = n <= 0 ? 0 : n % 6;
  return star.slice(5 - num, 10 - num);
}

/**
 * 在包含“下限”和“上限”的范围内钳制“数字”。
 * @category Number
 * @param value 要加紧的数字
 * @param upper 上限
 * @param lower 下限，默认值0
 * @example
 *
 * clamp(-10,5,-5)=>-5
 * clamp(10,3)=>5
 */
export function clamp(value: number, upper: number, lower: number = 0) {
  return Math.max(Math.min(value, upper), lower);
}

/**
 * 检查 n 是否在 start 与 end 之间，但不包括 end。 如果 end 没有指定，那么 start 设置为0。 如果 start 大于 end，那么参数会交换以便支持负范围。
 * @param n 要检查的值
 * @param end 结束范围
 * @param start 开始范围
 * @returns
 */
export function inRange(n: number, end: number, start?: number) {
  start = start || 0;
  const max = Math.max(start, end);
  const min = Math.min(start, end);
  return n >= min && n < max;
}

/**
 * 判断该数是否为整数
 * @param n 自然数
 * @returns boolean
 */
export function isInt(n: number) {
  return n % 1 === 0;
}

/**
 * 数字转中文
 * @param n
 * @returns
 */
export function toChineseNumber(n: number) {
  const strNums = n
    .toString()
    .replace(/\B(?=(\d{4})+$)/g, ",")
    .split(",");
  const chars = ["零", "一", "二", "三", "四", "五", "六", "七", "八", "九"];
  const units = ["", "十", "百", "千"];
  const bigUnits = ["", "万", "亿"];
  function handleZero(str: string) {
    return str.replace(/零{2,}/g, "零").replace(/零+$/g, "");
  }
  function _transform(strNum: string) {
    if (strNum === "0000") {
      return chars[0];
    }
    let res = "";
    for (let i = 0, len = strNum.length; i < len; i++) {
      const c = chars[+strNum[i]];
      let u = units[len - 1 - i];
      if (c === chars[0]) {
        u = "";
      }
      res += c + u;
    }
    res = handleZero(res);
    // res = res || chars[0];
    return res;
  }
  let result = "";
  for (let i = 0, len = strNums.length; i < len; i++) {
    const c = _transform(strNums[i]);
    let u = bigUnits[len - 1 - i];
    if (c === chars[0]) {
      u = "";
    }
    result += c + u;
  }
  // console.log(result);
  result = handleZero(result);
  return result;
}

/**
 * 数字转中文大写
 * @param n
 * @returns
 */
export function toBigChineseNumber(n: number) {
  const map = {
    零: "零",
    一: "壹",
    二: "贰",
    三: "叁",
    四: "肆",
    五: "伍",
    六: "陆",
    七: "柒",
    八: "捌",
    九: "玖",
    十: "拾",
    百: "佰",
    千: "仟",
    万: "万",
    亿: "亿",
  };
  const res = toChineseNumber(n);
  return (
    res
      .split("")
      .map((s) => map[s])
      .join("") + "元整"
  );
}

/**
 * 金额转中文大写
 * @category Number
 * @param money 金额数
 * @returns 中文大写
 * @example
 * moneyToBigChinese("4,455,509.03")=>'肆佰肆拾伍万伍仟伍佰零玖元零叁分'
 */
export function moneyToBigChinese(money: number | string) {
  const cnNums = ["零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"];
  const cnIntRadice = ["", "拾", "佰", "仟"];
  const cnIntUnits = ["", "万", "亿", "兆"];
  const cnDecUnits = ["角", "分"];
  const eWord = ["零", "元", "整"];
  let IntegerNum = "",
    DecimalNum = "",
    result = "";
  if (typeof money === "string") {
    const mon = money.trim().replace(/,/g, "");
    money = Number(mon);
  }
  if (Number.isNaN(money)) {
    throw new TypeError("请输入金额!");
  }
  const maxNum = 999999999999999.99;
  if (money > maxNum) {
    throw new RangeError("金额过大!");
  }

  if (money < 0) {
    result = "负";
    money = -money;
  }
  if (money.toString().indexOf(".") > -1) {
    money = money.toFixed(2);
    IntegerNum = money.toString().split(".")[0];
    DecimalNum = money.toString().split(".")[1];
  } else {
    IntegerNum = money.toString();
  }

  let t = 0;
  for (let i = 0, len = IntegerNum.length; i < len; i++) {
    const currentSite = len - i - 1;
    const currentData = IntegerNum.slice(i, i + 1);
    const bigUnit = currentSite / 4;
    const smallUnit = currentSite % 4;
    if (currentData === "0") {
      t++;
    } else {
      if (t > 0) {
        result += cnNums[0];
      }
      t = 0;
      result += cnNums[parseInt(currentData)] + cnIntRadice[smallUnit];
    }
    if (smallUnit === 0 && t < 4) {
      result += cnIntUnits[bigUnit];
      t = 0;
    }
  }
  result += eWord[1];

  if (DecimalNum === "00" || DecimalNum === "") {
    if (result === "") {
      result = eWord.join("");
    } else {
      result += eWord[2];
    }
    return result;
  } else {
    for (let i = 0; i < DecimalNum.length; i++) {
      const currentData = DecimalNum.slice(i, i + 1);
      if (currentData !== "0") {
        result += cnNums[parseInt(currentData)] + cnDecUnits[i];
      } else if (i === 0) {
        result += cnNums[parseInt(currentData)];
      }
    }
    return result;
  }
}

/**
 * 判断浮点数是否相等
 * @param num1 浮点数1
 * @param num2 浮点数2
 * @param precision 精度；默认为2；
 * @returns boolean
 * @example
 * floatEquals(0.33333,0.33334,2) => true
 */
export function floatEquals(num1: number, num2: number, precision = 2) {
  const EPSILON = Math.pow(10, -precision);
  return Math.abs(num1 - num2) < EPSILON;
}

/**
 * 根据区间线性转换目标值
 * @param num 待处理值
 * @param in_min 待处理值范围最小值
 * @param in_max 待处理值范围最大值
 * @param out_min 目标范围最小值
 * @param out_max 目标范围最大值
 * @returns 转换后的值
 */
export function getScale(
  num: number,
  in_min: number,
  in_max: number,
  out_min: number,
  out_max: number
) {
  num = clamp(num, in_min, in_max);
  const scale = (num - in_min) / (in_max - in_min);
  return out_min + scale * (out_max - out_min);
}
